package com.xyz.graph.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xyz.graph.common.BaseController;
import com.xyz.graph.common.CommonResponse;
import com.xyz.graph.common.annotation.OptimizeLog;
import com.xyz.graph.handler.Data2GraphServiceCallable;

import com.xyz.graph.handler.Edge2GraphServiceCallable;
import com.xyz.graph.handler.PreStartDoThing;
import com.xyz.graph.pojo.gdata.AddVertexParamPojo;
import com.xyz.graph.pojo.gdata.edgeParam.AddEdgeParamPojo;
import com.xyz.graph.pojo.responsepojo.FrontView;
import com.xyz.graph.pojo.responsepojo.Links;
import com.xyz.graph.pojo.responsepojo.NodeXY;
import com.xyz.graph.schema.GraphSchemaEnum;
import com.xyz.graph.service.BuiltinSearchOperation;
import com.xyz.graph.service.GDataService;
import com.xyz.graph.service.GSelectService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author: mxy
 * @date: 2020/03/02
 * @UPDATED BY
 **/
@Api(value = "图查询")
@Slf4j
@RequestMapping("gselect/v1/rest")
@RestController
@CrossOrigin(origins = "*", maxAge = 3600)
public class GraphSelectController extends BaseController {

    @Autowired
    private BuiltinSearchOperation builtinSearchOperation;
//
//    @Autowired
//    private DataTask dataTask;
    @Autowired
    private GSelectService gSelectService;

    @Autowired
    private GDataService gDataService;

    @GetMapping(value = "/addVE")
    @ApiOperation(value = "addVE", notes = "addVE")
    @OptimizeLog
    public void addVE(){
        ExecutorService excutor = Executors.newFixedThreadPool(10);
        List<AddVertexParamPojo> vertexParamPojoList = new ArrayList<>(3*5);
        List<AddEdgeParamPojo> edgeParamPojoArrayList = new ArrayList<>(5);
        //a person
        AddVertexParamPojo addVertexParamPojo = new AddVertexParamPojo();
        addVertexParamPojo.setEntityIdKey("personid");
        addVertexParamPojo.setEntityId("123");
        addVertexParamPojo.setLabelName("person");

        Map<String,String> map = new HashMap<>();
        map.put("personid","123");
        map.put("name","sdggfgs");
//        map.put("entitytype","person");
        addVertexParamPojo.setKeyValues(map);
        vertexParamPojoList.add(addVertexParamPojo);

        //b person
        AddVertexParamPojo addVertexParamPojo2 = new AddVertexParamPojo();
        addVertexParamPojo2.setEntityIdKey("personid");
        addVertexParamPojo2.setEntityId("123456");
        addVertexParamPojo2.setLabelName("person");

        Map<String,String> map2 = new HashMap<>();
        map2.put("personid","123456");
        map2.put("name","sdggfgs");
        addVertexParamPojo2.setKeyValues(map2);
        vertexParamPojoList.add(addVertexParamPojo2);

        //edge cohabit
        AddEdgeParamPojo addEdgeParamPojo = new AddEdgeParamPojo();
        addEdgeParamPojo.setLabelName("cohabit");
        addEdgeParamPojo.setEntitySrcIdKey("personid");
        addEdgeParamPojo.setEntitySrcId("123");
        addEdgeParamPojo.setEntityDstIdKey("personid");
        addEdgeParamPojo.setEntityDstId("123456");
        addEdgeParamPojo.setSrcIdColName("srcid");
        addEdgeParamPojo.setDstIdColName("dstid");
        Map<String,String> cohabitedge = new HashMap<>();
        cohabitedge.put("departmentid","asd1234123");

        addEdgeParamPojo.setKeyValues(cohabitedge);
        edgeParamPojoArrayList.add(addEdgeParamPojo);

        Future future =excutor.submit(new Data2GraphServiceCallable(vertexParamPojoList,gDataService));
        try {
            future.get();
            Future future_edge =excutor.submit(new Edge2GraphServiceCallable(edgeParamPojoArrayList,gDataService));
            future_edge.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    /**
     * @author: mxy
     * @date: 2020/03/14 12:36
     * @UPDATED BY
     **/
    @GetMapping(value = "/getMock")
    @ApiOperation(value = "模拟数据，前端可视化", notes = "前端可视化")
    @OptimizeLog
    public FrontView getMock(String msg){

        log.info("请求模拟数据");
        log.info("msg:{}",msg);

        FrontView frontView = new FrontView();
        Map<String, NodeXY> nodemap = new HashMap<>();

        NodeXY nodeXY = new NodeXY();
        nodeXY.setKey("1");
        nodeXY.setName("654234678456768");
        nodeXY.setType("person");
        nodemap.put("1",nodeXY);
        NodeXY nodeXY2 = new NodeXY();
        nodeXY2.setKey("2");
        nodeXY2.setName("6542133454545656");
        nodeXY2.setType("person");
        nodemap.put("2",nodeXY2);

        NodeXY nodeXY3 = new NodeXY();
        nodeXY3.setKey("新X88888");
        nodeXY3.setName("car123");
        nodeXY3.setType("car");
        nodemap.put("新X88888",nodeXY3);

        NodeXY nodeXY4 = new NodeXY();
        nodeXY4.setKey("hotel1");
        nodeXY4.setName("全季酒店");
        nodeXY4.setType("hotel");
        nodemap.put("hotel1",nodeXY4);

        NodeXY nodeXY5 = new NodeXY();
        nodeXY5.setKey("3");
        nodeXY5.setName("321564564654564");
        nodeXY5.setType("person");
        nodemap.put("3",nodeXY5);

        frontView.setNodes(nodemap);
        //links 关系
        List linklist = new ArrayList();
        Links links1 = new Links();
        links1.setSource("1");
        links1.setTarget("2");
        links1.setRela("朋友");
        links1.setType("123");
        linklist.add(links1);

        Links links2 = new Links();
        links2.setSource("1");
        links2.setTarget("新X88888");
        links2.setRela("拥有");
        linklist.add(links2);

        Links links3 = new Links();
        links3.setSource("2");
        links3.setTarget("hotel1");
        links3.setRela("入住");
        linklist.add(links3);

        Links links4 = new Links();
        links4.setSource("2");
        links4.setTarget("新X88888");
        links4.setRela("租用");
        linklist.add(links4);

        Links links5 = new Links();
        links5.setSource("1");
        links5.setTarget("3");
        links5.setRela("同住");
        linklist.add(links5);

        Links links6 = new Links();
        links6.setSource("2");
        links6.setTarget("1");
        links6.setRela("好朋友");
        links6.setType("123");
        linklist.add(links6);

        frontView.setLinks(linklist);
        return frontView;
    }


    @GetMapping(value = "/getDataByProp")
    @ApiOperation(value = "根据属性值=XX，查询关系和实体", notes = "属性值=XX")
    @OptimizeLog
    public FrontView getDataByProp(String msg,String step){
        String[] msgarr = msg.split("=");
        if(step==null){
            step = "2";
        }
        FrontView frontView = new FrontView();
        Map<String, NodeXY> nodemap = new HashMap<>();
        List<Links> linklist = new ArrayList();
        GraphTraversalSource g = PreStartDoThing.getG();
        List<ArrayList<HashMap<String,String>>> lists=builtinSearchOperation.entityExtend(g,msgarr[1],Integer.valueOf(step),null,msgarr[0],true);
//        HashMap<String, ArrayList<HashMap<String,String>>> map = gSelectService.entityExtend(g,msgarr[1],2,null,msgarr[0],true);
//        log.info(JSON.toJSONString(map));
        log.info("路径分析结果：{}",JSON.toJSONString(lists));
//        Map<String,Integer> relationcount = new HashMap<>();
        Map<String,List<Map<String,String>>> relationcountmap = new ConcurrentHashMap<>();

        lists.stream().forEach(hashMaps -> {
            hashMaps.stream().forEach(stringStringHashMap -> {
                String type = stringStringHashMap.get("parentType");
                //实体类型
                String entitytype = stringStringHashMap.get("type");
                String key = null;
                if("person".equals(entitytype)){
                    key = "personid";
                }else if("hotel".equals(entitytype)){
                    key = "hotelid";
                }else {
                    key = entitytype+"id";
                }

                String name = null;
                if("entity".equals(type)){
                    if(stringStringHashMap.get("name")!=null){
                        name = stringStringHashMap.get("name");
                    }else if(stringStringHashMap.get(entitytype+"name")!=null){
                        name = stringStringHashMap.get(entitytype+"name");
                    }else{
                        name = stringStringHashMap.get(key);
                    }
                }
                String keyvalue;
                if(stringStringHashMap.get(key)!=null){
                    keyvalue = stringStringHashMap.get(key);
                }else{
                    keyvalue = null;
                }

                if("entity".equals(type)&&keyvalue!=null){
                    NodeXY nodeXY = new NodeXY();
                    nodeXY.setKey(keyvalue);
                    nodeXY.setName(name);
                    nodeXY.setType(entitytype);
                    nodeXY.setProp(stringStringHashMap);
                    nodemap.put(keyvalue,nodeXY);
                }else if("relation".equals(type)){
                    //links 关系

                    Links links1 = new Links();
                    links1.setSource(stringStringHashMap.get("srcid"));
                    links1.setTarget(stringStringHashMap.get("dstid"));
                    if("kinship".equals(stringStringHashMap.get("type"))){
                        links1.setRela(stringStringHashMap.get("describe"));
                    }else{
                        links1.setRela(GraphSchemaEnum.valueOf(stringStringHashMap.get("type")).getChnname());
                    }
                    links1.setType(stringStringHashMap.get("type"));
                    linklist.add(links1);
                }

                //记录关系次数及详情
                if("relation".equals(stringStringHashMap.get("parentType"))){
                    String rela = GraphSchemaEnum.valueOf(stringStringHashMap.get("type")).getChnname();
                    String x =stringStringHashMap.get("srcid")+"_"+stringStringHashMap.get("dstid")+"_"+rela;
                    String x1 =stringStringHashMap.get("dstid")+"_"+stringStringHashMap.get("srcid")+"_"+rela;

                    if(relationcountmap.get(x)!=null){
                        List<Map<String,String>> temp= relationcountmap.get(x);
//                        List<Map<String,String>> listtemp = new ArrayList<>();
                        if(temp!=null&&temp.size()>0) {
//                        log.info("listtemp:"+listtemp.size()+","+JSON.toJSONString(listtemp,SerializerFeature.DisableCircularReferenceDetect));
                            relationcountmap.put(x, distinctLinks(temp,stringStringHashMap));
                        }
                    }else if(relationcountmap.get(x1)!=null){
                        List<Map<String,String>> temp= relationcountmap.get(x1);
                        if(temp!=null&&temp.size()>0) {
//                        log.info("listtemp:"+listtemp.size()+","+JSON.toJSONString(listtemp,SerializerFeature.DisableCircularReferenceDetect));
                            relationcountmap.put(x, distinctLinks(temp,stringStringHashMap));
                        }
                    }else{
                        relationcountmap.put(x,new ArrayList<>());
                        List<Map<String,String>> temp= relationcountmap.get(x);
                        temp.add(stringStringHashMap);
                        relationcountmap.put(x,temp);
                    }
                }

            });

        });//end

        relationcountmap.entrySet().forEach(stringListEntry -> {
            List<Map<String,String>> newlist = new ArrayList<>(stringListEntry.getValue().stream().collect(Collectors.toCollection(()->
                    new TreeSet<>(Comparator.comparing(o -> o.get("vid"))))));
            relationcountmap.put(stringListEntry.getKey(),newlist);
        });
        List<Links> newlist = new ArrayList<>(linklist.stream().collect(Collectors.toCollection(()->
                new TreeSet<>(Comparator.comparing(o -> o.getSource()+o.getTarget())))));


        newlist.stream().forEach(links -> {
            String key = links.getSource()+"_"+links.getTarget()+"_"+links.getRela();
            String key1 = links.getTarget()+"_"+links.getSource()+"_"+links.getRela();

            if(relationcountmap.get(key)!=null){
                String relacount = String.valueOf(relationcountmap.get(key).size());
                links.setRelacount(relacount);
                links.setQmd(getQmd(relacount,"tongfang"));
                links.setProps(relationcountmap.get(key));
            }else if(relationcountmap.get(key1)!=null){
                String relacount = String.valueOf(relationcountmap.get(key1).size());
                links.setRelacount(relacount);
                links.setQmd(getQmd(relacount,"tongfang"));
                links.setProps(relationcountmap.get(key1));
            }

        });
        frontView.setNodes(nodemap);
        frontView.setLinks(newlist);
        log.info("relamap:"+ JSON.toJSONString(relationcountmap, SerializerFeature.DisableCircularReferenceDetect));
        log.info(JSON.toJSONString(frontView));
        return frontView;
    }


    //关系去重，用来计算亲密度以及查看关系详情列表
    public List<Map<String,String>> distinctLinks(List<Map<String,String>> temp,Map<String,String> stringStringHashMap){
        AtomicBoolean cannadd = new AtomicBoolean(true);
        List<Map<String, String>> listtemp = new ArrayList<>();

        temp.stream().forEach(stringStringMap -> {
            AtomicInteger num = new AtomicInteger(0);
            stringStringHashMap.entrySet().forEach(stringStringEntry -> {
                if (!stringStringEntry.getKey().equals("vid")) {
                    String value1 = stringStringEntry.getValue();
                    String value2 = stringStringMap.get(stringStringEntry.getKey());
                    if (value1.equals(value2)) {
                        num.addAndGet(1);
                    }

                }

            });
            if (num.get() == stringStringHashMap.size() - 1) {
                cannadd.set(false);
            }
        });

        if (cannadd.get()) {
            listtemp.add(stringStringHashMap);
            temp.addAll(listtemp);
        }
        cannadd.set(true);
        listtemp.clear();
        return temp;
    }

    public String getQmd(String relationcount,String type){
        double qmdnum = 0.00;
        double relacount = Integer.parseInt(relationcount);
        DecimalFormat decimalFormat = new DecimalFormat("0.00");
        if("tongfang".equals(type)){
            qmdnum = relacount/(1+relacount);
        }else if ("feitongfang".equals(type)){
            qmdnum = relacount/(2+relacount);
        }
        return decimalFormat.format(qmdnum);
    }

}
